#!/usr/bin/perl -w

#########################################################################
#									#
# Name:	 	clstartup (Perl Program)				#
#									#
# Completed:								#
#									#
# Updated:								#
#									#
# Author:	Simon Edwards, Proprius Consulting Ltd.			#
#									#
# Arguments:	start|stop						#
#		start	Start cluster services				#
#		stop	Stop cluster services				#
#									#
# Purpose:	Is called from init to start or stop the cluster	#
# 		services on the local machine.				#
#									#
#		The functionality offered depends on the contents of	#
#		a configuration file called /etc/clstartup.xml		#
#		which supports the following options.			#
#									#
#		<?xml version="1.0" standalone="yes"?>			#
#		<clstart_config>					#
#		  <allow_single_node>yes|no</allow_single_node>		#
#		  <background_startup>yes|no</background_startup>	#
#		  <join_existing_cluster>yes|no</join_existing_cluster>	#
#		  <autostart_apps>yes|no</autostart_apps>		#
#		  <timeout>NNN</timeout>				#
#		</clstart_config>					#
#		allow other nodes to join existing cluster.		#
#									#
# Limitations:	1 - lacks flexibility - very few options supported.	#
#									#
# Environment:	None.							#
#									#
# Exit Codes:	0 - The node joined or formed cluster successfully.	#
#		1 - One or more fatal errors have occured.		#
# 									#
# Version:	@(#)1.0.0 Original (SE)>				#
#									#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
#									#
# Complete SOCM Version History						#
#									#
# Version:	1.0.0							#
# Date:		18th August, 2004.					#
# Author:	Simon Edwards, Proprius Consulting Ltd.			#
# Change:	Original.						#
#									#
#########################################################################

BEGIN {
	push @INC,"/usr/local/cluster/lib/perl";
}

use XML::Simple;
use Digest::MD5;
use Data::Dumper;
use Sys::Hostname;
use cld_client;
use clutils;
use File::Basename;

$ARG_verbose=1;
$ARG_start=0;
$ARG_stop=0;
$_PRG=basename($0);

sub abort_if_bad {
my $varname=$_[0];

	if(!defined($$varname)) {
		return;
	}
	$v=$$varname;
	if($v ne "yes" and $v ne "no") {
		errmsg("Invalid setting for $varname ($v) - should be 'yes' or 'no'.",4);
	}
}

if(!exists($ARGV[0])) {
	errmsg("Usage: $_PRG start|stop",1);
}

if($ARGV[0] ne "start" && $ARGV[1] ne "stop") {
	errmsg("Usage: $_PRG start|stop",1);
}

$ARG_start=1 if($ARGV[0] eq "start");
$ARG_stop=1 if($ARGV[0] eq "stop");
#########################################################################
# Check to see if the configuration file exists, and abort if not.	#
#########################################################################

if(! -f "/etc/cluster/clstartup.xml") {
	errmsg("Error: /etc/cluster/clstartup.xml missing.",2);
	exit(2);
}

eval '$xml=XMLin("/etc/cluster/clstartup.xml");';
if($@) {
	if($@ =~ /mismatched tag at line ([0-9]+),/m) {
		errmsg("Invalid setting at line $1 of clstartup.xml",3);
	}
	errmsg("Invalid XML file definition: $@",3);
}

#########################################################################
# Check which variables are set, and set suitable defaults if not.	#
#########################################################################

$allow_single_node=undef;
$background_startup=undef;
$join_existing_cluster=undef;
$autostart_apps=undef;
$timeout=undef;

if(exists($xml->{allow_single_node})) {
	$allow_single_node=$xml->{allow_single_node};
}
if(exists($xml->{background_startup})) {
	$background_startup=$xml->{background_startup};
}
if(exists($xml->{join_existing_cluster})) {
	$join_existing_cluster=$xml->{join_existing_cluster};
}
if(exists($xml->{autostart_apps})) {
	$autostart_apps=$xml->{autostart_apps};
}
if(exists($xml->{timeout})) {
	$timeout=$xml->{timeout};
}

#########################################################################
# Check for values given and abort if not suitable.			#
#########################################################################

abort_if_bad("allow_single_node");
abort_if_bad("background_startup");
abort_if_bad("join_existing_cluster");
abort_if_bad("autostart_apps");

if(defined($timeout) && $timeout !~ /^[0-9]+$/) {
	errmsg("timeout value must be an integer.",5);
}

$allow_single_node="no" if !defined($allow_single_node);
$background_startup="no" if !defined($background_startup);
$join_existing_cluster="no" if !defined($join_existing_cluster);
$autostart_apps="no" if !defined($autostart_apps);

logmsg("Allow Single Node      : $allow_single_node");
logmsg("Background Startup     : $background_startup");
logmsg("Join Existing Cluster  : $join_existing_cluster");
logmsg("Auto-start Applications: $autostart_apps");
logmsg("Override Timeout       : $timeout") if defined($timeout);

#########################################################################
# We first validate that the cluster configuration exists, and has	#
# been configured correctly without changes.				#
#########################################################################

$ARG_config="/etc/cluster/clconf.xml" if !defined($ARG_config);
if(! -f $ARG_config) {
	errmsg("The configuration file $ARG_config does not exist",4);
}

($xml,$b,@errs)=validate_cluster_cfg($ARG_config);
if(!defined($xml)) {
	foreach $cerr (@errs) {
		errmsg($cerr);
	}
	errmsg("$_PRG exiting with error code $b.",$b);
}

#########################################################################
# Get some details we need to start communicating with the other node.	#
#########################################################################

$clustername=$xml->{global}->{name};
$server_port=get_allocated_resource(hostname,"PORT","__CLUSTER__","%%CLDAEMON",undef);
if($server_port<0) {
	errmsg("Cluster daemon port not defined - has clbuild been run?",22);
}
$server_key=$xml->{global}->{key};
@nodes=sort(keys(%{$xml->{node}}));
if($nodes[0] eq hostname) {
	$rnode=$nodes[1];
	$lnode=$nodes[0];
} else {
	$rnode=$nodes[0];
	$lnode=$nodes[1];
}

#########################################################################
# We firstly need to check to see if either of the nodes is running	#
# a cluster daemon. If so we abort unless the join_existing_cluster is	#
# set to "yes".								#
#########################################################################

logmsg("Checking that the cluster is not already running...");
$rlocal=0;
$conn=cld_client->new(PORT => $server_port,INET => $lnode, KEY => $server_key);
if(defined($conn)) {
	if(defined($conn)) {
		$resp=$conn->send_msg(MSG => "ECHO check=y", TIMEOUT => 15);
		$conn->close_session;
		if(defined($resp)) {
			$rlocal=1;
		}
	}
}

if($rlocal) {
	errmsg("Cluster is already running on local node.",20);
}

$rremote=0;
$conn2=cld_client->new(PORT => $server_port,INET => $rnode, KEY => $server_key);
if(defined($conn2)) {
	if(defined($conn2)) {
		$resp=$conn2->send_msg(MSG => "ECHO check=y", TIMEOUT => 15);
		$conn2->close_session;
		if(defined($resp) && $resp ne "FORMING") {
			$rremote=1;
		}
	}
}

#########################################################################
# If rremote is set then we check join_existing_cluster.		#
#########################################################################

if($rremote && $join_existing_cluster eq "no") {
	errmsg("Cluster is running on remote node, but the");
	errmsg("join_existing_cluster value is set to \"no\".",21);
}
logmsg("Cluster is running on $rnode already - will attempt to join...") if $rremote;
logmsg("All nodes in cluster are down, will attempt cluster formation...") if ! $rremote;

$cmd="/sbin/cluster/cldaemon --detach ";
if($rremote) {
	$cmd.="--join ";
} else {
	$cmd.="--form ";
}
if($allow_single_node eq "yes") {
	$cmd.="--force ";
}
if(defined($timeout)) {
	$cmd.="--timeout $timeout ";
} else {
	if(exists($xml->{timings}->{cluserform})) {
		$timeout=$xml->{timings}->{clusterform}
	} else {
		$timeout=299;
	}
}

if(defined($xml->{global}->{logdir})) {
	$logdir=$xml->{global}->{logdir};
} else {
	$logdir="/var/log/cluster";
}

if(! -d "$logdir") {
	errmsg("Log directory $logdir does not exist!",23);
}

$cmd.="--file $logdir/cldaemon-$clustername.log ";

#########################################################################
# We now indicate the command we are about to run on the local machine.	#
#########################################################################

logmsg("Will run the following command:");
logmsg("$cmd");

`$cmd >/dev/null 2>&1`;

$ctime=time;
$maxtime=$ctime+$timeout;
$up=0;
while(1) {
	last if time>$maxtime;
	$conn=cld_client->new(PORT => $server_port,INET => $lnode, KEY => $server_key);
	if(defined($conn)) {
		if(defined($conn)) {
			$resp=$conn->send_msg(MSG => "ECHO check=y", TIMEOUT => 15);
			$conn->close_session;
			if(defined($resp)) {
				logmsg("Response was: $resp");
				if($resp eq "UP") {
					$up=1;
					last;
				}
			}
		}
	}
	select(undef,undef,undef,2);
}

if(!$up) {
	errmsg("Node failed to join or form cluster.",25);
}

logmsg("Node has joined or formed cluster.");

if($autostart_apps eq "no") {
	exit(0);
}

#########################################################################
# Fistly we work out whether the other node in the cluster is 		#
# running, and if not then we will start the applications, otherwise	#
# this node will only start the applications if it is the first node	#
# in the cluster.							#
#########################################################################

$start_apps=0;
$resp=$conn->send_msg(MSG => "GET_NODE_STATE NODE=$rnode", TIMEOUT => 10);
$conn->close_session;
if(!defined($resp) || $resp ne "UP") {
	$start_apps=1;
}

if(defined($resp) && $resp eq "UP") {
	logmsg("lnode=$lnode,rnode=$rnode");
	if(("$lnode" cmp "$rnode") < 0) {
		$start_apps=1;
	}
}

if(!$start_apps) {
	logmsg("Node $rnode will attempt to start applications.");
	exit(0);
}

#########################################################################
# We now get a list of applications that are registered with the	#
# cluster that are defined to autostart...				#
#########################################################################

$resp=$conn->send_msg(MSG => "AUTOSTARTLIST", TIMEOUT => 30);
$conn->close_session;

($status,$apps)=split(/\s+/,$resp);
if(!defined($status) || $status ne "OK") {
	warnmsg("No cluster applications will be automatically started.");
	exit(0);
}

$errs=$oks=0;
foreach $start_app (split(/,/,$apps)) {
	logmsg("Attempting to start autostart application \"$start_app\"...");
	$cmd="/sbin/cluster/clrunapp --application $start_app";
	system($cmd);
	if($?>>8!=0) {
		logwarn("Application \"$start_app\" failed to start correctly.");
		$errs++;
	} else {
		$oks++;
	}
}
warnmsg("Failed to start $errs autostart applications") if $errs;
logmsg("Successfully started $oks autostart applications") if $oks;

exit(0);
